/**
 * All right reserved@Raygo 2022~2030
 */

package com.raygo.jfxdemo.view;

import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 * TableView示例
 *
 * @author Raygo
 * @since 2022年3月2日
 */
public class TableViewUI {
    /**
     * 打开窗口
     *
     * @throws Exception 异常
     */
    public void show() {
        Stage stage = new Stage();
        // 表格视图初始化
        TableView<Person> tableView = initTableView();

        // 操作按钮
        Button addLine = new Button("添加行");
        addLine.setOnMouseClicked((event) -> {
            // 添加行数据
            tableView.getItems().add(new Person("张三", 15));
            tableView.getItems().add(new Person("李四", 22));
        });
        Button singleSelect = new Button("单选模式");
        singleSelect.setOnMouseClicked((event) -> {
            // 设置选择模式为单行
            tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        });
        Button multiSelect = new Button("多选模式");
        multiSelect.setOnMouseClicked((event) -> {
            // 设置多行选择模式
            tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        });

        // 界面
        HBox topBar = new HBox();
        topBar.getChildren().addAll(addLine, singleSelect, multiSelect);
        VBox body = new VBox(tableView);
        VBox vbox = new VBox();
        vbox.getChildren().addAll(topBar, body);
        Scene scene = new Scene(vbox);
        stage.setScene(scene);
        stage.setWidth(640.0d);
        stage.setHeight(480.0d);
        stage.setTitle("TableView");
        stage.show();
    }

    private TableView<Person> initTableView() {
        // 创建一个表格视图
        TableView<Person> tableView = new TableView<>();
        tableView.setEditable(true);

        // 创建列
        TableColumn<Person, String> column1 = new TableColumn<>("姓名");
        column1.setCellValueFactory(new PropertyValueFactory<>("Name"));
        column1.setEditable(false);
        column1.setCellFactory(TextFieldTableCell.forTableColumn());
        TableColumn<Person, String> column2 = new TableColumn<>("年龄");
        column2.setCellValueFactory(new PropertyValueFactory<>("age"));
        column2.setEditable(true);
        column2.setCellFactory((tableColumn) -> new EditingCell<Person>());

        // 嵌套列
        TableColumn<Person, String> address = new TableColumn<>("地址");
        TableColumn<Person, String> country = new TableColumn<>("国家");
        TableColumn<Person, String> city = new TableColumn<>("城市");
        // 把子列添加到父列
        address.getColumns().add(country);
        address.getColumns().add(city);
        // 设置子列的渲染器，父列不需要设置
        country.setCellValueFactory(new PropertyValueFactory<>("country"));
        city.setCellValueFactory(new PropertyValueFactory<>("city"));
        city.setCellFactory(ComboBoxTableCell.<Person, String>forTableColumn("北京", "上海", "广州"));
        // 添加列到表格视图
        tableView.getColumns().add(address);

        // 设置列宽
        column1.setPrefWidth(120.0d);
        column2.setPrefWidth(200.0d);

        // 添加列
        tableView.getColumns().add(column1);
        tableView.getColumns().add(column2);
        // tableView.getColumns().addAll(column1, column2);

        // 无行数据显示时的占位符
        tableView.setPlaceholder(new Label("空白"));

        // 获取选中的行序号
        tableView.getSelectionModel().getSelectedIndex();
        // 获取选中的行数据
        tableView.getSelectionModel().getSelectedItem();
        // 获取选中的多行数据
        ObservableList<Person> selectedItems = tableView.getSelectionModel().getSelectedItems();
        // 取消选择
        tableView.getSelectionModel().clearSelection();
        // 监听选中行的变化
        selectedItems.addListener(new ListChangeListener<Person>() {
            @Override
            public void onChanged(Change<? extends Person> change) {
                System.out.println("Selection changed: " + change.getList());
            }
        });

        // 序号列
        TableColumn<Person, String> column0 = new TableColumn<>("序号");
        column0.setCellFactory((tableColumn) -> {
            TableCell<Person, String> tableCell = new TableCell<>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    this.setText(null);
                    this.setGraphic(null);
                    if (!empty) {
                        this.setText(String.valueOf(this.getIndex() + 1));
                    }
                }
            };
            return tableCell;
        });

        // 使能排序
        column0.setSortable(true);
        // 禁用排序
        column0.setSortable(false);
        // 排序类型设置为升序
        column0.setSortType(TableColumn.SortType.ASCENDING);
        // 排序类型设置为降序
        column0.setSortType(TableColumn.SortType.DESCENDING);
        // 添加列
        tableView.getColumns().add(0, column0);

        return tableView;
    }

    /**
     * 失去焦点自动提交
     *
     * @author Raygo
     * @since 2022年3月3日
     * @param <T>
     */
    public class EditingCell<T> extends TableCell<T, String> {
        private TextField textField;

        public EditingCell() {
        }

        @Override
        public void startEdit() {
            if (!isEmpty()) {
                super.startEdit();
                createTextField();
                setText(null);
                setGraphic(textField);
                textField.selectAll();
            }
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();

            setText((String) getItem());
            setGraphic(null);
        }

        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
                setGraphic(null);
            } else {
                if (isEditing()) {
                    if (textField != null) {
                        textField.setText(getString());
                    }
                    setText(null);
                    setGraphic(textField);
                } else {
                    setText(getString());
                    setGraphic(null);
                }
            }
        }

        @Override
        public void commitEdit(String newValue) {
            if (!isEditing() && !newValue.equals(this.getItem())) {
                TableView<T> table = this.getTableView();
                if (this.getTableView() != null) {
                    TableColumn<T, String> col = this.getTableColumn();
                    CellEditEvent<T, String> event = new CellEditEvent<T, String>(table,
                            new TablePosition<T, String>(table, this.getIndex(), col), TableColumn.editCommitEvent(),
                            newValue);
                    Event.fireEvent(col, event);
                }
            }
            super.commitEdit(newValue);
            updateItem(newValue, false);
        }

        private void createTextField() {
            textField = new TextField(getString());
            textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
            textField.focusedProperty().addListener((ob, old, now) -> {
                if (!now) {
                    commitEdit(textField.getText());
                }
            });
        }

        private String getString() {
            return getItem() == null ? "" : getItem().toString();
        }
    }
}
